package models

import (
	"github.com/jinzhu/gorm"
)

// Commodity 商品表
type Commodity struct {
	ID                     string `gorm:"primary_key"`
	No                     string
	CommodityType          string
	Name                   string
	Colour                 string
	MerchandiseSafetyStock uint
	PackgeNum              uint
	Weight                 float32
	PackgeCube             string
	PackgeWeight           float32
	Logo                   string
	Supply                 string
	Remark                 string
	Location               string
	Number                 uint
	Unit                   string
	PresaleNumber          uint
	SalesVolume            uint
	Size                   string
	PurchasePrice          float32
	StockNumber            float32
}

// CommodityDetail 商品详情表，包含库存是否够字段
type CommodityDetail struct {
	ID                     string `gorm:"primary_key"`
	No                     string
	CommodityType          string
	Name                   string
	Colour                 string
	MerchandiseSafetyStock uint
	PackgeNum              uint
	Weight                 float32
	PackgeCube             string
	PackgeWeight           float32
	Logo                   string
	Supply                 string
	Remark                 string
	Location               string
	Number                 uint
	PresaleNumber          uint
	SalesVolume            uint
	Size                   string
	PurchasePrice          float32
	Shortage               bool
	CollectNum             uint
}

// CommoditySale 商品表，如果有StockNumber字段的话，就用StockNumber表示库存，Number用来表示订单里的采购量，或者出货量
type CommoditySale struct {
	ID                     string `gorm:"primary_key"`
	No                     string
	CommodityType          string
	Name                   string
	Colour                 string
	MerchandiseSafetyStock uint
	PackgeNum              uint
	Weight                 float32
	PackgeCube             string
	PackgeWeight           float32
	Logo                   string
	Supply                 string
	Remark                 string
	Location               string
	Number                 uint
	Unit                   string
	PresaleNumber          uint
	SalesVolume            uint
	Size                   string
	PurchasePrice          float32
	StockNumber            float32
}

// CommodityComponents 商品配件比例表
type CommodityComponents struct {
	CommodityID string
	ComponentID string
	Number      uint
}

// CreateCommodity 创建新商品
func CreateCommodity(commodity *Commodity, ponents []CommodityComponents) error {
	tx := db.Begin()
	err := tx.Table("commodities").Create(commodity).Error
	if err != nil {
		tx.Rollback()
		return err
	}
	if len(ponents) != 0 {
		for _, v := range ponents {
			v.CommodityID = commodity.ID
			err = tx.Table("commodity_components").Create(&v).Error
			if err != nil {
				tx.Rollback()
				return err
			}
		}
	}
	tx.Commit()
	return nil
}

// DeleteCommodity 删除商品
func DeleteCommodity(commodityID string) error {
	tx := db.Begin()

	err := tx.Table("commodity_components").Where(
		"commodity_id = ?", commodityID).Delete(CommodityComponents{}).Error
	if err != nil {
		tx.Rollback()
		return err
	}

	err = tx.Table("commodities").Where(
		"id = ?", commodityID).Delete(Commodity{}).Error
	if err != nil {
		tx.Rollback()
		return err
	}

	tx.Commit()
	return nil
}

// UpdateCommodity 更新商品信息
func UpdateCommodity(commodityID string, commodity *Commodity, ponents []CommodityComponents) error {
	tx := db.Begin()

	err := tx.Table("commodities").Where("id = ?", commodityID).Updates(*commodity).Error
	if err != nil {
		tx.Rollback()
		return err
	}

	err = tx.Table("commodity_components").Where(
		"commodity_id = ?", commodityID).Delete(CommodityComponents{}).Error
	if err != nil {
		tx.Rollback()
		return err
	}

	if len(ponents) != 0 {
		for _, v := range ponents {
			v.CommodityID = commodityID
			err = tx.Table("commodity_components").Create(&v).Error
			if err != nil {
				tx.Rollback()
				return err
			}
		}
	}

	tx.Commit()
	return nil

}

// CommodityInfo 商品信息，包括配件信息
type CommodityInfo struct {
	Commodity
	Ponents []Component
}

// GetCommoditiesWithPurchasePrice 获取所有商品信息（包含进价）
func GetCommoditiesWithPurchasePrice() (commodities []CommodityInfo, err error) {
	err = db.Table("commodities").Select(
		"id, no,commodity_type, name, colour, merchandise_safety_stock, packge_num, weight," +
			"packge_cube,packge_weight,logo,supply,remark,location,number,presale_number,sales_volume,size,purchase_price").Find(&commodities).Error
	if err != nil {
		return
	}

	for index, v := range commodities {
		err = db.Raw(`select t2.id,t2.no, t2.name, t2.supply,t2.purchase_price, t1.number from commodity_components as t1 left outer join 
				components as t2 on t1.component_id = t2.id where t1.commodity_id = ?`, v.ID).Scan(&commodities[index].Ponents).Error
		if err != nil {
			return
		}
	}
	return
}

// GetCommodities 获取所有商品信息 // 库存警告那个接口
func GetCommodities() (commodities []CommodityDetail, err error) {
	err = db.Table("commodities").Select(
		"id, no,commodity_type,name, colour, merchandise_safety_stock, logo,supply,location, number, presale_number, sales_volume, size,purchase_price,packge_num,weight,packge_cube,packge_weight,remark").Find(&commodities).Error
	return
}

// GetCommoditiesStock 获取所有商品信息 // 库存警告那个接口
func GetCommoditiesStock() (commodities []CommodityDetail, err error) {
	err = db.Table("commodities").Select(
		"id,no, commodity_type,name, colour, merchandise_safety_stock, logo,supply,location, number, presale_number, sales_volume, size,purchase_price,packge_num,weight,packge_cube,packge_weight,remark").Find(&commodities).Error
	// 0，查出所有配件数量，commodity_components ，map结构：component_id，num
	components, _ := GetComponents()
	componentMap := Reset(components)
	for index, _ := range commodities { //  库存警告那个接口
		ID := commodities[index].ID
		commodity := GetCommodity(ID)
		// 1，直接比较下,库存和安全库存量，如果库存多，说明够了
		number := commodity.Number
		safetyStock := commodity.MerchandiseSafetyStock
		if number > safetyStock {
			commodities[index].Shortage = false
			continue
		}
		// 2，拿出商品的配件比
		commodityComponents := []CommodityComponents{}
		_ = db.Table("commodity_components").Where("commodity_id= ?", ID).Find(&commodityComponents)
		// 3，计算下配件能组成几个成品
		flag := true            // 表示配件数量能不能凑成一个成品
		var collectNum uint = 0 //计数看能组成几个成品
		if commodityComponents != nil && len(commodityComponents) > 0 {
			for true {
				//这里面是一个商品配件的循环
				flag = JudgeComponentEnough(commodityComponents, componentMap)
				if !flag {
					break
				}
				// 如果商品配件的量管够，那就继续继续那件商品的配件
				collectNum++
			}
		}
		// flag表示不能凑成一个成品，还要确实一次都没有凑成个成品过
		if !flag && collectNum == 0 {
			commodities[index].Shortage = true
			continue
		}
		// 4,将计算的结果与商品的库存量相加，最后与安全库存相比较
		if number+collectNum <= safetyStock {
			commodities[index].Shortage = true
		} else {
			commodities[index].Shortage = false
			commodities[index].Number = number + collectNum
		}
		commodities[index].CollectNum = collectNum

	}
	return
}

// 减配件数量看看能不能凑成一个成品
func JudgeComponentEnough(commodityComponents []CommodityComponents, componentMap map[string]uint) bool {
	subComponentMap := map[string]uint{} // 一旦第二个配件不够的时候，组不成成品了，那么第一个配件刚减去的数量就应该给他补回去
	flag := true                         // 表示配件数量能不能凑成一个成品
	for _, commodityComponent := range commodityComponents {
		if !flag { // 一旦第二个配件不够的时候，组不成成品了，那么第一个配件刚减去的数量就应该给他补回去
			for key, value := range subComponentMap {
				componentMap[key] = componentMap[key] + value
			}
			break
		}
		if componentMap[commodityComponent.ComponentID] == 0 {
			flag = false
			break
		}
		if componentMap[commodityComponent.ComponentID] <= commodityComponent.Number {
			flag = false
		} else {
			componentMap[commodityComponent.ComponentID] = componentMap[commodityComponent.ComponentID] - commodityComponent.Number
			subComponentMap[commodityComponent.ComponentID] = commodityComponent.Number
		}
	}
	return flag
}

// HaveCommodity 查询是否有此商品
func HaveCommodity(id string) bool {
	return !db.Table("commodities").Where("id = ?", id).First(&Commodity{}).RecordNotFound()
}

// GetCommodity 获取商品信息
func GetCommodity(id string) *Commodity {
	var commodity Commodity
	db.Table("commodities").Where("id = ?", id).Select(
		"id,no,commodity_type, name, colour, merchandise_safety_stock, logo,supply,location, number, presale_number, sales_volume, size,purchase_price,packge_num,weight,packge_cube,packge_weight,remark").First(&commodity)
	return &commodity
}

func getPresaleNumber(tx *gorm.DB, goodsID string) (number uint, err error) {
	info := struct {
		PresaleNumber uint
	}{}
	err = tx.Table("commodities").Select("presale_number").Where("id = ?", goodsID).Scan(&info).Error
	number = info.PresaleNumber
	return
}

func updatePresaleNumber(tx *gorm.DB, goodsID string, number uint) error {
	return tx.Table("commodities").Where("id = ?", goodsID).Update("presale_number", number).Error
}

func getNumber(tx *gorm.DB, goodsID string) (number uint, err error) {
	info := struct {
		Number uint
	}{}
	err = tx.Table("commodities").Select("number").Where("id = ?", goodsID).Scan(&info).Error
	number = info.Number
	return
}

func updateNumber(tx *gorm.DB, goodsID string, number uint) error {
	return tx.Table("commodities").Where("id = ?", goodsID).Update("number", number).Error
}

func getSalesVolume(tx *gorm.DB, goodsID string) (number uint, err error) {
	info := struct {
		SalesVolume uint
	}{}
	err = tx.Table("commodities").Select("sales_volume").Where("id = ?", goodsID).Scan(&info).Error
	number = info.SalesVolume
	return
}

func updateSalesVolume(tx *gorm.DB, goodsID string, number uint) error {
	return tx.Table("commodities").Where("id = ?", goodsID).Update("sales_volume", number).Error
}

// CommodityType 商品分类表
type CommodityType struct {
	ID   string `gorm:"primary_key"`
	Name string
}

// GetCommodityType 获取所有商品分类
func GetCommodityType() (commodityType []CommodityType, err error) {
	err = db.Table("commodity_types").Find(&commodityType).Error
	return
}

type Location struct {
	Location string
}

// GetLocation list所有位置
func GetLocation() (locations []Location, err error) {
	err = db.Raw("select location from (select location from commodities union select location from components)t").Scan(&locations).Error
	return
}
